import SDK from 'sxran-tool-sdk/Index'
import XeBase from 'sxran-tool-core/lib/XeBase';
import createObservable from './createObservable';
// uni-app的实现
// import BroadcastChannelImp from "sxran-tool-uniapp/lib/UniAppBroadcastChannel";
// import WebSocketImpl from "sxran-tool-uniapp/lib/UniAppWebSocket";


/**
 * 额外单独引用一下，使得这个js可以单独使用。
 * @type {Index}
 */
const sdk = new SDK();

// 响应式，代替vuex的简单使用。
// 不能是简单的属性，比如我收到消息之后，其他的组件页面应该同步收到
const state = createObservable({
    // 会话数据
    conversation: {
        dataList: [],
        hasMore: true,
        loading: false, // 加锁防止意外重复请求时出错,
        /**
         * 统计所有消息的未读数.
         * 里面封装啦，一些内部逻辑处理。
         */
        unreadCount(){
            // return state.conversation.dataList.length;
            let conversationDatas = this.dataList
            return conversationDatas.reduce((sum, item, index, array) => sum + item.unreadCount, 0)
        },
        findConversation(conversation_id){
            return this.dataList.find(i => i.id == conversation_id);
        },
        getConversation(conversation_id){
            let conversationDatas = this.dataList.filter(i => i.id == conversation_id);
            console.log('-----getConversation-----datas-------',this.dataList,conversationDatas,conversation_id);
            if (conversationDatas.length === 0) {
                // 确保会有会话生成：
                // 对应的业务逻辑是，如删除了会话，想再次和好友或者群聊聊天时需要有一个会话展示出来。
                let conversation = {
                    id : conversation_id,
                    unreadCount : 0,
                    msgList : [],
                    hasMore: true,
                    loading: false, // 加锁防止意外重复请求时出错,
                };
                this.dataList.push(conversation);
                return conversation;
            }else{
                // return conversationDatas[0];
                let conversationDatum = conversationDatas[0];
                if(!conversationDatum.hasOwnProperty("hasMore")){
                    conversationDatum.hasMore = true;
                }
                if(!conversationDatum.hasOwnProperty("loading")){
                    conversationDatum.loading = false;
                }
                this.sortMsgList(conversationDatum?.msgList);
                return conversationDatum;
            }
        },
        getMsgList(conversation_id){
            // this.sortMsgList(conversation_id);
            return this.sortMsgList(this.getConversation(conversation_id)?.msgList);
        },
        sortMsgList(msgList){
            // return this.getConversation(conversation_id).msgList.sort((a,b)=>{
            return msgList;
            return msgList.sort((a,b)=>{
                // if(orderBy.create_time == 'asc'){
                //     return a.create_time - b.create_time
                // }else{
                //     return b.create_time - a.create_time
                // }
                return a.serviceCreateTime - b.serviceCreateTime;
            });
        },
        pushConversationObj(newConversationObj){
            return this.pushConversation(newConversationObj.id,newConversationObj);
        },
        pushConversation(conversation_id,newConversationObj){
            let conversation =  this.findConversation(conversation_id); // this.getConversation(msg.conversation);
            // 【BUG】接收方第一次收到消息，会话列表标题和头像就是空的
            if(!conversation){
                conversation = newConversationObj;
                this.dataList.push(conversation);
            }
            return conversation;
        },
        pushMsg(msg,remind){
            let conversation = this.pushConversationObj(msg.newConversationObj);
            // 移到 this.getConversation 里面啦。
            // if(!conversation){
            //     conversation = {
            //         id : msg.conversation,
            //         unreadCount : 0,
            //         msgList : []
            //     };
            //     this.dataList.push(conversation);
            // }
            //
            if(!conversation.msgList){
                conversation.msgList = [];
            }
            conversation.msgList.push(msg);
            // 上层应用来决定是否添加。 自己接受到的群聊消息不添加。
            if(remind){
                conversation.unreadCount += 1;
            }
            conversation.updateTime = new Date().getTime();
            switch (msg.msgType){
                case 0 :
                    conversation.lastMsgNote = msg.content
                    break;
                default :
                    conversation.lastMsgNote = '收到一条消息';
                    break;
            }

        }

    },
    // 正在对话的会话id
    currentConversationId: false,
    // 全局响应式心跳，用于更新消息距离当前时长 等
    heartbeat: '',
    // 好友列表
    friend: {
        dataList: [],
        hasMore: true
    },
    // 群列表
    group: {
        dataList: [],
        hasMore: true
    },
    // 系统通知消息
    notification: {
        dataList: [],
        hasMore: true,
        get: ({
                  type,
                  excludeType
              } = {}) => {
            const notificationDatas = state.notification.dataList
            if (notificationDatas) {
                return notificationDatas.reduce((sum, item) => {
                    // 指定需要的类型
                    if (type) {
                        //兼容字符串和数组
                        // typeof type == 'string' ? type = [type] : ''
                        if(!Array.isArray(type)){
                            type = [type];
                        }
                        if (type.includes(item.type)) {
                            sum.push(item)
                        }
                        // 排查指定的类型
                    } else if (excludeType) {
                        //兼容字符串和数组
                        // typeof excludeType == 'string' ? excludeType = [excludeType] : ''
                        if(!Array.isArray(excludeType)){
                            excludeType = [excludeType];
                        }
                        if (!excludeType.includes(item.type)) {
                            sum.push(item)
                        }
                    } else {
                        sum.push(item)
                    }
                    return sum
                }, [])
            } else {
                return []
            }
        },
        unreadCount(param = {}) {
            let notificationDatas = this.get(param)
            let unreadCount = notificationDatas.reduce((sum, item, index, array) => {
                if (!item.isRead) {
                    sum++
                }
                return sum
            }, 0)
            return unreadCount;
            // console.log('最新的未读数:', unreadCount, data);
            // 注意：在非tabbar页面无法设置 badge
            // if (unreadCount === 0) {
            //     uni.removeTabBarBadge({
            //         index: 2,
            //         complete: (e) => {
            //             // console.log(e)
            //         }
            //     })
            // } else {
            //     uni.setTabBarBadge({
            //         index: 2,
            //         text: unreadCount + '',
            //         complete: (e) => {
            //             // console.log(e)
            //         }
            //     })
            // }

            // if (unreadCount) {
            //     return unreadCount + ''
            // } else {
            //     return ''
            // }
        },
        unreadCountStr(param = {}) {
            let unreadCount = this.unreadCount(param);
            if (unreadCount) {
                return unreadCount + ''
            } else {
                return ''
            }
        }
    },
    //存储所有出现过的用户信息，包括群好友信息
    usersInfo: {},
    cacheUserInfo(data){
        let userId = data.userId;
        if (userId) {
            if (!this.usersInfo[userId]) {
                this.usersInfo[userId] = data;
            }
        }
    },
    //是否为pc宽屏
    isWidescreen: false,
    //系统信息
    // systemInfo: {},
    // #ifndef H5
    indexDB: false,
    // #endif
    audioContext: false,
    // sqlite数据库是否已经打开
    dataBaseIsOpen: false,
    // 记录socket连接次数（用于处理，断开重连）
    socketOpenIndex: 0
})
// let _this = null
let connect = null;
let broadcastChannel = null; // 不对外暴露使用。
const broadcastKey = "sxran-im-broadcast";
const userInfoKey = "sxran-im-userInfo";
export default {
    // 接收从外部传入的this，并赋给_this
    // receive(vm) {
    //     _this = vm
    // },
    state : state,
    /**
     * 广播通知的key
     */
    getBroadcastKey(){
        return broadcastKey;
    },
    /**
     * 获得登陆之后的用户信息。
     * @returns {*}
     */
    getSelfUser(){
        return sdk.storageUtil.getItem(userInfoKey);
    },
    /**
     * 获得登陆之后的用户id。
     * @returns String
     */
    getSelfUserId(){
        return this.getSelfUser().id;
    },
    // broadcastChannel : sdk.broadcastChannelUtil.getBroadcastChannel(broadcastKey),
    /**
     * 将有的sdk的内容放进来。
     * @param sdk
     * @see #init
     */
    copySdk(sdk){
        this.init(sdk.webSocketUtil,sdk.broadcastChannelUtil,sdk.storageUtil);
    },
    /**
     * 初始化
     * @param webSocketImpl
     * @param broadcastChannelImp
     * @param storageImp
     */
    init(webSocketImpl,broadcastChannelImp,storageImp){
        sdk.setApi(SDK.BASE_NAME,new XeBase());
        // sdk.setApi(SDK.WEBSOCKET_NAME,new WebSocketImpl());
        sdk.setApi(SDK.WEBSOCKET_NAME,webSocketImpl);
        // sdk.setApi(SDK.BROADCAST_CHANNEL_NAME,new BroadcastChannelImp());
        sdk.setApi(SDK.BROADCAST_CHANNEL_NAME,broadcastChannelImp);
        sdk.setApi(SDK.STORAGE_NAME,storageImp);
    },
    /** 判断是否是群聊
     *
     */
    isGroup(conversation){
        if(conversation.type){
            return conversation.type == 1;
        }else{
            // 兼容,没有type的群组内容。
            return conversation.id.startsWith('1_');
        }
    },
    /**
     * 判断是否是连接的。
     */
    isConnect(){
        return connect;
    },
    /**
     * 连接ws
     * @param wsUrl
     */
    connect(wsUrl){
        const that = this;
        this.close();
        broadcastChannel = sdk.broadcastChannelUtil.getBroadcastChannel(broadcastKey); // 不对外暴露使用。
        //
        let websocketConfig = {
            "wsUrl" : wsUrl,
            onclose : () =>{
                that.handLog('onclose');
            },
            onerror: () =>{
                that.handLog('onerror');
            },
            onmessage: (mes) =>{
                let mesObj = sdk.toStringJSON(mes);
                that.handLog('onmessage-接收到的im信息为:',mesObj);
                // 内置的业务逻辑处理
                const data = mesObj.data;
                const command = mesObj.command;
                if(data){
                    switch (command) {
                        case -5: // 登陆的用户信息缓存。
                            sdk.storageUtil.setItem(userInfoKey,data);
                            state.cacheUserInfo(data);
                            // 登陆后获取收到的通知内容。
                            that.getNotificationList({
                                toId : that.getSelfUserId()
                            });
                            break;
                        case  11 : // 接收到的聊天信息。
                        case -11 : //
                            state.conversation.pushMsg(data, that.getSelfUserId() !=  data.from);
                            break;
                        case -7: // 群聊新上线的用户，有些是公共聊天室。  不会一下提供所有入群的用户信息，因为本身也不确定。 所有这里监听到进入群聊的用户就缓存起来。
                            state.cacheUserInfo(data.user);
                            break;
                        case -17: // 获得用户信息的操作。
                            state.cacheUserInfo(data);
                            break;
                        case -21: // 获取群组在线用户列表
                            data.forEach(user => {state.cacheUserInfo(user)});
                            break;
                        case -23: // 获取会话列表
                            state.conversation.dataList =  data;
                            state.conversation.hasMore = false;
                            break;
                        case 26: // 接收到的通知。
                            let notificationFindIndex26 = state.notification.dataList?.findIndex(item => item.uuid === data.uuid );
                            if(   notificationFindIndex26 === -1  ){
                                state.notification.dataList.push(data);
                            }else{
                                // state.notification.dataList[notificationFindIndex26] = data; // 不会触发响应式。
                                state.notification.dataList.splice(notificationFindIndex26,1,data);
                            }
                            break;
                        case -28: // 获取通知列表
                            let datas =  data || [];
                            state.notification.dataList = datas.map(item => {
                                item.from = JSON.parse(item.fromInfo);
                                item.extras = {};
                                return item;
                            })
                            break;
                        case -32: //
                            state.conversation.pushConversationObj(data);
                            break;
                        case -34: //
                            state.friend.dataList.push(...data);
                            state.friend.hasMore = false;
                            break;
                        case -35: //
                            state.group.dataList.push(...data);
                            state.group.hasMore = false;
                            break;
                        default :
                            break;
                    }
                }else{
                    that.handError("IM的返回的失败信息:",{command,mes});
                }
                // 发送广播消息，可以使用额外的增强扩展。
                // this.broadcastChannel.postMessage(mesObj);
                broadcastChannel.postMessage(mesObj);
            },
            onopen: () =>{
                that.handLog('onopen');

            }
        };
        connect = sdk.webSocketUtil.connect(websocketConfig);
    },
    /**
     * 处理日志
     * @param tag
     * @param logObj
     */
    handLog(tag,logObj){
        console.log(tag,logObj);
    },
    /**
     * 处理错误异常
     * @param tag
     * @param errObj
     */
    handError(tag,errObj){
        console.error(tag,errObj);
    },
    /**
     * 删除租户的标识。 获取原系统的内容。
     * @param id
     * @returns {*}
     */
    dumpTenant(id){
        let res = id.substring( id.indexOf( ':') + 1 );
        return res;
    },
    /**
     *
     * 发送通用的消息。
     * 【通过回调形式改成，形成 请求/响应 模型 】
     * 【仅仅只是发送的时候，性能较好】
     * @param data [对应json对象]
     * @param callback [对应的data数据] 需要和对应的command保持一致。
     * @see #sendAsync 如果确认一定有返回值可以用它。
     */
    send(data,callback){
        if(callback){
            let broadcast = sdk.broadcastChannelUtil.getBroadcastChannel(broadcastKey);
            // broadcast.setOnMessageOnceCall((res)=>{ // 如果有延迟就会接受不到对应的数据
            broadcast.setOnMessageCall((res)=>{
                if( Math.abs(res.command)   === Math.abs(data.cmd) ){
                    callback(res);
                    broadcast.close();
                }
            });
        }
        connect.send(sdk.baseUtil.toJSONString(data));
    },
    /**
     * 设置监听器函数内容。
     * @param commands
     * @param callback
     */
    setMonitor(commands,callback){
        if(callback){
            let broadcast = sdk.broadcastChannelUtil.getBroadcastChannel(broadcastKey);
            // broadcast.setOnMessageOnceCall((res)=>{ // 如果有延迟就会接受不到对应的数据
            broadcast.setOnMessageCall((res)=>{
                if( commands.includes(res.command) ){
                    callback(res);
                }
            });
        }
    },
    /**
     * http的ws模型响应内容
     * @param data
     * @returns {Promise<unknown>}
     */
    sendAsync(data){
        return new Promise((resolve, reject) => {
            try{
                connect.send(sdk.baseUtil.toJSONString(data));
                let broadcast = sdk.broadcastChannelUtil.getBroadcastChannel(broadcastKey);
                broadcast.setOnMessageCall((res)=>{
                    if( Math.abs(res.command)   === Math.abs(data.cmd) ){
                        resolve(res);
                        broadcast.close();
                    }
                });
            }catch (e) {
                reject(e);
            }
        });

    },
    /**
     * 手动关闭消息通道。
     */
    close(){
        connect?.close();
        if(broadcastChannel){
            broadcastChannel.close();
        }
    },
    //////////////////////////////////////////////////////
    //
    //                 以下是通过 sendAsync 获取的业务操作功能。 类似于http的接口形式：
    //      1. go开头代表没有返回值，仅仅只是后台处理。
    //      2. 其他默认都是有返回值操作的。
    //////////////////////////////////////////////////////
    /**
     * 心跳
     */
    goHeartbeat(){
        this.send({cmd: 13, hbbyte: "-127"});
    },
    /**
     * 鉴权操作
     * @param token
     * @param extras
     */
    goAuth(token,extras){
        this.send({cmd: 3, token,extras});
    },
    /**
     * 获取指定用户基础信息。
     * @param userId 用户的id
     * @param type 0:在线, 1:离线, 2:所有用户(在线+离线)
     * @param extras   额外参数
     * @returns {*}
     */
    getUserInfo(userId,type,extras){
         return this.sendAsync({cmd: 17, type,userId : this.dumpTenant(userId),extras});
    },
    /**
     * 获取群组在线用户列表请求; 【 在线：代表绑定群组，并且进入了群组 】
     * @param groupId
     * @returns {*|Promise<unknown>}
     */
    getGroupOnlineUsers(groupId){
        return this.sendAsync({cmd: 21, groupId });
    },
    /**
     * 获取指定用户的消息。
     * @param fromUserId 用户的id
     * @param type 0:离线消息,1:历史消息
     * @param offset 分页偏移量
     * @param count 分页数量
     * @param beginTime 消息区间开始时间
     * @param endTime 消息区间结束时间
     * @param extras   额外参数
     * @returns {*}
     */
    getUserMessage(fromUserId,type,offset,count, beginTime,endTime,extras){
        return this.sendAsync({cmd: 19, type,fromUserId,userId : this.getSelfUserId() ,offset,count, beginTime,endTime,extras});
    },
    /**
     * 获取指定群组的消息。
     * @param groupId 群组id
     * @param type 0:离线消息,1:历史消息
     * @param offset 分页偏移量
     * @param count 分页数量
     * @param beginTime 消息区间开始时间
     * @param endTime 消息区间结束时间
     * @param extras   额外参数
     * @returns {*}
     */
    getGroupMessage(groupId,type,offset,count, beginTime,endTime,extras){
        return this.sendAsync({cmd: 19, type,groupId,userId : this.getSelfUserId(),offset,count, beginTime,endTime,extras});
    },
    /**
     * 执行私聊
     * @param to 目标人
     * @param content  消息内容
     * @param msgType  消息类型（0:text、1:image、2:voice、3:vedio、4:music、5:news ...）
     * @param extras   额外参数
     * @returns {*} 可能被后台处理过发送的消息内容
     */
    doChat(to,content,msgType,extras){
        return this.sendAsync({cmd: 11, chatType : 2 , content,createTime : new Date().getTime() , from : this.getSelfUser().id , msgType ,to ,extras});
    },
    /**
     * 执行群聊
     * @param groupId 目标人
     * @param content  消息内容
     * @param msgType  消息类型（0:text、1:image、2:voice、3:vedio、4:music、5:news ...）
     * @param extras   额外参数
     * @returns {*} 可能被后台处理过发送的消息内容
     */
    doGroupChat(groupId,content,msgType,extras){
        return this.sendAsync({cmd: 11, chatType : 1, content,createTime : new Date().getTime() , from : this.getSelfUserId() , msgType ,groupId ,extras});
    },
    /**
     * 获得会话的消息列表。
     * @param data 输入自己需要的额外参数逻辑，可以不传
     * @returns {*}
     */
    getMessageList(data = {}){
        data.cmd = 22;
        return this.sendAsync( data );
    },
    /**
     * 获取会话列表。
     * @param data 输入自己需要的额外参数逻辑，可以不传
     * @returns {*}
     */
    getConversationList(data = {}){
        data.cmd = 23;
        return this.sendAsync( data );
    },
    /**
     * 搜索用户列表的接口。
     * @param data 输入自己需要的额外参数逻辑，可以不传
     * @returns {*}
     */
    searchUserList(data = {}){
        data.cmd = 24;
        return this.sendAsync( data );
    },
    /**
     * 搜索群组列表的接口。
     * @param data 输入自己需要的额外参数逻辑，可以不传
     * @returns {*}
     */
    searchGroupList(data = {}){
        data.cmd = 25;
        return this.sendAsync( data );
    },
    /**
     * 已读通知处理。
     * @param uuid 唯一id
     * @returns {*}
     */
    readNotification(uuid){
        let data = {
            uuid,
            cmd : 27,
            toId : this.getSelfUserId()
        };
        // 先设置为已读，再调用后端接口。
        let find = state.notification.dataList?.find(item => item.uuid === uuid );
        find.isRead = 1;
        return this.sendAsync( data );
    },
    /**
     * 获取通知列表。
     * @param data 通知的id
     * @returns {*}
     */
    getNotificationList(data = {}){
        data.cmd = 28;
        return this.sendAsync( data );
    },
    /**
     * 添加好友申请。
     * @param data 输入自己需要的额外参数逻辑，可以不传
     * @returns {*}
     */
    addFriendInvite(data){
        data.cmd = 29;
        return this.sendAsync( data );
    },

    /**
     * 同意好友申请请求。
     * @param uuid uuid标识。
     * @param message 回复消息。
     * @returns {*}
     */
    acceptFriendApplication(uuid , message){
        // 先设置为已读，再调用后端接口。
        let find = state.notification.dataList?.find(item => item.uuid === uuid );
        find.isRead = 1;
        find.state  = 100;
        // 接收到的对象用户id内容。
        let fromUid = find.from.userId;
        let data = {
            cmd : 30,
            fromUid , message
        }
        return this.sendAsync( data );
    },
    /**
     * 拒绝好友申请请求。
     * @param uuid 对应的用户id。
     * @param message 回复消息。
     * @returns {*}
     */
    refuseFriendApplication(uuid , message){
        // 先设置为已读，再调用后端接口。
        let find = state.notification.dataList?.find(item => item.uuid === uuid );
        find.isRead = 1;
        find.state  = -100;
        let fromUid = find.fromUid;
        let data = {
            cmd : 31,
            fromUid , message
        }
        return this.sendAsync( data );
    },
    /**
     * 生成会话id。
     * @param chatType 对应的用户id。
     * @param originalTo 回复消息。
     * @returns {*}
     */
    generateConversationId(chatType,originalTo){
        let data = {
            cmd : 32,
            chatType , originalTo
        }
        return this.sendAsync( data );
    },
    clearUnreadCount(conversationId){
        // 先设置为已读，再调用后端接口。
        let data = {
            cmd : 33,
            id : conversationId,
            userId : this.getSelfUserId()
        }
        state.conversation.getConversation(conversationId).unreadCount = 0; 
        return this.sendAsync( data );
    },
    /**
     * 获取好友列表
     * @returns {Promise<unknown>}
     */
    getFriendList(){
        let data = {
            cmd : 34,
        }
        return this.sendAsync( data );
    },
    /**
     * 获取群组列表
     * @returns {Promise<unknown>}
     */
    getGroupList(){
        let data = {
            cmd : 35,
        }
        return this.sendAsync( data );
    },


    /**
     * 添加群组申请。
     * @param data 输入自己需要的额外参数逻辑，可以不传
     * @returns {*}
     */
    addGroupInvite(data){
        data.cmd = 36;
        return this.sendAsync( data );
    },
    /**
     * 同意好友申请请求。
     * @param uuid uuid标识。
     * @param message 回复消息。
     * @returns {*}
     */
    acceptGroupApplication(uuid , message){
        // 先设置为已读，再调用后端接口。
        let find = state.notification.dataList?.find(item => item.uuid === uuid );
        find.isRead = 1;
        find.state  = 100;
        // 接收到的对象用户id内容。
        let fromUid = find.from.userId;
        // TODO find.groupId
        let data = {
            cmd : 37,
            userId : fromUid ,
            groupId : find?.extras?.groupId,
            message
        }
		console.log('---acceptGroupApplication-----',data);
        return this.sendAsync( data );
    },
    /**
     * 拒绝好友申请请求。
     * @param uuid 对应的用户id。
     * @param message 回复消息。
     * @returns {*}
     */
    refuseGroupApplication(uuid , message){
        // 先设置为已读，再调用后端接口。
        let find = state.notification.dataList?.find(item => item.uuid === uuid );
        find.isRead = 1;
        find.state  = -100;
        console.log('-----find------',find);
        let fromUid = find.from.userId;
        let data = {
            cmd : 38,
            userId : fromUid ,
            groupId : find?.extras?.groupId ,
            message
        }
        return this.sendAsync( data );
    },
    /**
     * 创建群聊请求
     * @param data
     * @returns {Promise<unknown>}
     */
    createGroup(data){
        data.cmd = 39;
        return this.sendAsync( data );
    },
    //////////////////////////////////////////
    //  以下是封装啦一些业务逻辑的处理操作。
    /////////////////////////////////////////
    /**
     * 优先从缓存中获取用户信息，如果没有的话才调用接口。
     * @param userId
     * @returns {*|Promise<unknown>}
     */
    getUserInfoByCache(userId){
        let usersInfo = state.usersInfo[userId];
        if(usersInfo){
            return new Promise((resolve, reject) => {
                resolve(usersInfo);
            });
        }else{
            return this.getUserInfo(userId);
        }
    },
    loadMoreMessage(conversation_id){
        let conversation = state.conversation.getConversation(conversation_id);
        let msgListElement = conversation?.msgList[0];
        conversation.loading = true;
        return this.getMessageList({
            conversation : conversation_id,
            maxTime : msgListElement ?  msgListElement.serviceCreateTime : new Date().getTime() ,
            offset : 1,
            count : 20
        }).then( (res) => {
            console.log('-----getMessageList-----',res );
            if(res.data.length === 0){
                conversation.hasMore = false;
            }else{
                conversation?.msgList.unshift(...res.data);
            }
            return new Promise((resolve, reject) => {
                resolve(res.data);
            });
        }).catch((err) => {
            this.handError('loadMoreMessage',err);
        }).finally(()=> {
            conversation.loading = false;
        });
    }
}
